CodeMirror.defineMode("yaml", function () {

    var cons = ['true', 'false', 'on', 'off', 'yes', 'no'];
    var keywordRegex = new RegExp("\\b((" + cons.join(")|(") + "))$", 'i');

    return {
        token: function (stream, state) {
            var ch = stream.peek();
            var esc = state.escaped;
            state.escaped = false;
            /* comments */
            if (ch == "#") {
                stream.skipToEnd();
                return "comment";
            }
            if (state.literal && stream.indentation() > state.keyCol) {
                stream.skipToEnd();
                return "string";
            } else if (state.literal) {
                state.literal = false;
            }
            if (stream.sol()) {
                state.keyCol = 0;
                state.pair = false;
                state.pairStart = false;
                /* document start */
                if (stream.match(/---/)) {
                    return "def";
                }
                /* document end */
                if (stream.match(/\.\.\./)) {
                    return "def";
                }
                /* array list item */
                if (stream.match(/\s*-\s+/)) {
                    return 'meta';
                }
            }
            /* pairs (associative arrays) -> key */
            if (!state.pair && stream.match(/^\s*([a-z0-9\._-])+(?=\s*:)/i)) {
                state.pair = true;
                state.keyCol = stream.indentation();
                return "atom";
            }
            if (state.pair && stream.match(/^:\s*/)) {
                state.pairStart = true;
                return 'meta';
            }

            /* inline pairs/lists */
            if (stream.match(/^(\{|\}|\[|\])/)) {
                if (ch == '{')
                    state.inlinePairs++;
                else if (ch == '}')
                    state.inlinePairs--;
                else if (ch == '[')
                    state.inlineList++;
                else
                    state.inlineList--;
                return 'meta';
            }

            /* list seperator */
            if (state.inlineList > 0 && !esc && ch == ',') {
                stream.next();
                return 'meta';
            }
            /* pairs seperator */
            if (state.inlinePairs > 0 && !esc && ch == ',') {
                state.keyCol = 0;
                state.pair = false;
                state.pairStart = false;
                stream.next();
                return 'meta';
            }

            /* start of value of a pair */
            if (state.pairStart) {
                /* block literals */
                if (stream.match(/^\s*(\||\>)\s*/)) {
                    state.literal = true;
                    return 'meta';
                }
                ;
                /* references */
                if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) {
                    return 'variable-2';
                }
                /* numbers */
                if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) {
                    return 'number';
                }
                if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) {
                    return 'number';
                }
                /* keywords */
                if (stream.match(keywordRegex)) {
                    return 'keyword';
                }
            }

            /* nothing found, continue */
            state.pairStart = false;
            state.escaped = (ch == '\\');
            stream.next();
            return null;
        },
        startState: function () {
            return {
                pair: false,
                pairStart: false,
                keyCol: 0,
                inlinePairs: 0,
                inlineList: 0,
                literal: false,
                escaped: false
            };
        }
    };
});

CodeMirror.defineMIME("text/x-yaml", "yaml");
